package com.scs.application.modules.upms.controller;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.scs.application.consts.SecurityConsts;
import com.scs.application.core.global.GlobalParamService;
import com.scs.application.core.model.CommonResult;
import com.scs.application.core.mvc.controller.BaseController;
import com.scs.application.core.utils.CookieUtils;
import com.scs.application.core.utils.DateUtils;
import com.scs.application.core.utils.Md5Utils;
import com.scs.application.core.utils.StringUtils;
import com.scs.application.modules.upms.dto.UsernameAndPasswordLoginDTO;
import com.scs.application.modules.upms.dto.WechatBindUserDTO;
import com.scs.application.modules.upms.dto.WxmpLoginDTO;
import com.scs.application.modules.upms.entity.WxmpAccount;
import com.scs.application.modules.upms.entity.WxmpMember;
import com.scs.application.modules.upms.login.token.BaseAccessToken;
import com.scs.application.modules.upms.login.token.UsernamePasswordAccessToken;
import com.scs.application.modules.upms.service.LoginService;
import com.scs.application.modules.upms.service.WxmpAccountService;
import com.scs.application.modules.upms.service.WxmpMemberService;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.Date;
import java.util.Map;

/**
 * @Description:
 */
@Slf4j
@RestController
@RequestMapping("/upms/wechat")
public class WechatController extends BaseController {

    @Autowired
    private WxmpAccountService wxmpAccountService;
    @Autowired
    private WxmpMemberService wxmpMemberService;

    @Autowired
    private WxMpService wxMpService;


    @Autowired
    private LoginService loginService;


    /**
     * 生成网页授权链接并重定向
     *
     * @param mpKey       公众号标识
     * @param redirectUri 重定向URI
     * @param response
     * @throws IOException
     */
    @GetMapping("/to_oauth2")
    public void toOauth2(@RequestParam("mpKey") String mpKey,
                         @RequestParam(name = "redirectUri", required = false) String redirectUri,
                         HttpServletResponse response) throws IOException {
        wxMpService.switchoverTo(mpKey);
        WxMpConfigStorage wxMpConfigStorage = wxMpService.getWxMpConfigStorage();

        String redirectUrl = wxMpConfigStorage.getOauth2redirectUri();
        if (redirectUrl.indexOf("?") != -1) {
            redirectUrl += String.format("&appId=%s&mpKey=%s", wxMpConfigStorage.getAppId(), mpKey);
        } else {
            redirectUrl += String.format("?appId=%s&mpKey=%s", wxMpConfigStorage.getAppId(), mpKey);
        }
        String url = wxMpService.oauth2buildAuthorizationUrl(redirectUrl, WxConsts.OAuth2Scope.SNSAPI_BASE, redirectUri);
        response.sendRedirect(url);
    }

    /**
     * @param mpKey
     * @param code
     * @param state
     * @throws IOException
     */
    @GetMapping("/oauth2")
    public void oauth2(
            @RequestParam("mpKey") String mpKey,
            @RequestParam(name = "code", required = false) String code,
            @RequestParam("state") String state) throws IOException {
        Map<String, String[]> parameterMap = request().getParameterMap();

        if (StringUtils.isBlank(code)) {
            CommonResult<Object> result = CommonResult.error("请在微信公众号打开");
            response().getWriter().write(JSON.toJSONString(result));
            return;
        }

        wxMpService.switchoverTo(mpKey);
        WxMpConfigStorage wxMpConfigStorage = wxMpService.getWxMpConfigStorage();
        String appId = wxMpConfigStorage.getAppId();

        WxMpOAuth2AccessToken accessToken = null;
        try {
            accessToken = wxMpService.oauth2getAccessToken(code);
            WxmpAccount account = wxmpAccountService.getById(mpKey);

            /**
             *  当前微信是否已经绑定用户，如果已绑定，跳转至首页，否则跳转到绑定页
             */
            String openId = accessToken.getOpenId();
            CookieUtils.addCookie(response(), "wxmp_key", mpKey);
            CookieUtils.addCookie(response(), "wxmp_app_id", appId);
            CookieUtils.addCookie(response(), "wxmp_open_id", openId);

            int existCount = wxmpMemberService.count(Wrappers.<WxmpMember>query().eq("open_id", openId));

            String homeUrl = account.getHomeUrl();

            // 如果已经有cookie存在且未过期，不再尝试自动登录
            Cookie cookie = CookieUtils.getCookie(request(), SecurityConsts.TOKEN_HEADER);
            String redirectUrl = StringUtils.defaultIfBlank(state, homeUrl);
            if (cookie != null && cookie.getMaxAge() != 0) {
                log.info("current is login state, redirect to home page, current domain is {}", GlobalParamService.getInstance().getDomain());
                response().sendRedirect(redirectUrl);
                return;
            }
            if (existCount > 0) {
                log.info("wechat is binded user, redirect to home page, current domain is {}", GlobalParamService.getInstance().getDomain());
                WxmpLoginDTO loginDTO = new WxmpLoginDTO();
                loginDTO.setAppId(appId).setOpenId(openId);

                BaseAccessToken token = null;
                Exception loginExp = null;
                try {
                    token = loginService.login(loginDTO);

                } catch (Exception e) {
                    loginExp = e;
                    log.error("微信自动登录失败", e);

                } finally {
                    if (token == null || loginExp != null) {
                        log.info("wechat auto login failed, redirect to error page");
                        response().sendRedirect(account.getLoginUrl());
                        return;
                    }
                }

                CookieUtils.clearAllCookie(request(), response());
                CookieUtils.addCookie(response(), SecurityConsts.TOKEN_HEADER, token.getJti(), token.getExpiredDate());
                response().sendRedirect(redirectUrl);
                return;
            }

            log.info("wechat is not yet bind user, redirect to login page");
            redirectUrl = account.getLoginUrl();
            if (redirectUrl.indexOf("?") != -1) {
                redirectUrl += String.format("&appId=%s&openId=%s", appId, openId);
            } else {
                redirectUrl += String.format("?appId=%s&openId=%s", appId, openId);
            }
            response().sendRedirect(redirectUrl);
            return;

        } catch (WxErrorException e) {
            log.error("微信授权失败", e);
            response().sendError(401);
        }
    }

    /**
     * 自动登录成功跳转
     *
     * @param redirectUrl
     * @param token
     * @throws IOException
     */
    private void autoLoginSuccess(String redirectUrl, BaseAccessToken token) throws IOException {
        /*if (redirectUrl.indexOf("?") != -1) {
            redirectUrl += String.format("&%s=%s&%s=%s", URLEncoder.encode(SecurityConsts.TOKEN_HEADER), token.getJti(),
                    "expiredDate", DateUtils.format(token.getExpiredDate(), "yyyy-MM-dd HH:mm:ss"));
        } else {
            redirectUrl += String.format("?%s=%s&%s=%s", URLEncoder.encode(SecurityConsts.TOKEN_HEADER), token.getJti(),
                    "expiredDate", DateUtils.format(token.getExpiredDate(), "yyyy-MM-dd HH:mm:ss"));
        }*/
        response().sendRedirect(redirectUrl);
    }

    @PostMapping("/bind")
    public CommonResult bindUser(@RequestBody @Valid WechatBindUserDTO dto) {
        // 允许绑定多个
        int existCount = wxmpMemberService.count(Wrappers.<WxmpMember>query().eq("open_id", dto.getOpenId()));
        /*if (existCount > 0) {
            return CommonResult.error("此微信号已经绑定了一个用户，不可重复绑定");
        }*/

        WxMpService wxMpService = wxmpAccountService.getWxMpServiceFromAppId(dto.getAppId());

        UsernameAndPasswordLoginDTO loginDTO = new UsernameAndPasswordLoginDTO();
        loginDTO.setUsername(dto.getUsername()).setPassword(Md5Utils.md5(dto.getPassword()));

        UsernamePasswordAccessToken accessToken = (UsernamePasswordAccessToken) loginService.login(loginDTO);

        WxmpMember member = new WxmpMember();
        member.setAppId(dto.getAppId()).setOpenId(dto.getOpenId())
                .setUserId(accessToken.getUserId())
                .setLoginKey(accessToken.getLoginKey())
                .setGmtJoined(new Date());
        wxmpMemberService.saveCascadeById(member);

        WxMpTemplateMessage msg = new WxMpTemplateMessage();
        try {
            msg.setTemplateId("DhcG3OFW2KrF5tpnCc3iyGfyAdM2X9mzTKs0Tq2LI70");
            msg.setToUser(dto.getOpenId());
            msg.addData(new WxMpTemplateData("send_date", DateUtils.format(new Date(), "yyyy-MM-dd")));
            String templateMsg = wxMpService.getTemplateMsgService().sendTemplateMsg(msg);
            System.out.println(templateMsg);
        } catch (WxErrorException e) {
            e.printStackTrace();
        }

        return CommonResult.ok("绑定成功", accessToken);
    }

    @DeleteMapping("/unbind")
    public CommonResult unbind() {

        return CommonResult.ok("解绑成功");
    }


}
